package demo.bean;

import demo.bean.annotations.Component;
import demo.bean.annotations.Scope;
import demo.bean.model.BeanDefinition;
import demo.bean.util.ScanUtil;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {

    private static Map<String,Object> singletonMap = new HashMap<String,Object>();

    private static Map<String,BeanDefinition> prototype = new HashMap<String, BeanDefinition>();

    public BeanFactory(String packageName) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        initPrototype(packageName);

        initSingleton();
    }

    private void initPrototype(String packageName) throws ClassNotFoundException {
        List<String> scan = ScanUtil.scanf(packageName);
        for (String file:scan){
            Class<?> clazz = Class.forName(file);
            if (clazz.isAnnotationPresent(Component.class)){
                BeanDefinition bean = new BeanDefinition();
                String comValue = componentMethod(clazz);
                bean.setId(comValue);
                String scopeValue = scopeMethod(clazz);
                bean.setScope(scopeValue);
                bean.setClazz(clazz);
                //判断是否初始化这个注解
                Method init = initMethod(clazz);
                bean.setInitMethod(init);
                //判断是否销毁这个注解
                Method destroy = destroyMethod(clazz);
                bean.setDestroyMethod(destroy);
                prototype.put(comValue,bean);
            }
        }

    }

    private void initSingleton(){
        for (String key:prototype.keySet()){
            BeanDefinition beanDefinition = prototype.get(key);
            if (beanDefinition.getScope().equals("singleton")){
                Object bean = null;
                try {
                    bean = createBean(beanDefinition);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                singletonMap.put(key,bean);
            }
        }
    }

    private static String componentMethod(Class<?> clazz){
        Component annotation = clazz.getAnnotation(Component.class);
        String value = null;
        if (annotation.value().equals("")){
           value = clazz.getSimpleName().substring(0,1).toLowerCase()+clazz.getSimpleName().substring(1,clazz.getSimpleName().length());
        }else{
            value = annotation.value();
        }
        return value;
    }

    private static String scopeMethod(Class<?> clazz){
        String scope = null;
        if (clazz.isAnnotationPresent(Scope.class)){
            scope = clazz.getAnnotation(Scope.class).value();
        }else{
            scope = "singleton";
        }
        return scope;
    }
    //创建Bean实例
    private Object createBean(BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getClazz();
        Object bean = null;
        try {
            bean = clazz.newInstance();
            executeInit(bean,beanDefinition);
            InjectMethod.execute(bean,clazz,this);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return bean;
    }

    //判断初始化这个注解
    private Method initMethod(Class<?> clazz){
        Method method = null;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m:declaredMethods){
            if (m.isAnnotationPresent(PostConstruct.class)) {
                method = m;
            }
        }
        return method;
    }

    //判断销毁这个注解
    private Method destroyMethod(Class<?> clazz){
        Method method = null;
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m:declaredMethods){
            if (m.isAnnotationPresent(PreDestroy.class)){
                method = m;
            }
        }
        return method;
    }

    //执行销毁方法
    public void executeDestroy(){
        for (String key:prototype.keySet()){
            BeanDefinition beanDefinition = prototype.get(key);
            if ("singleton".equals(beanDefinition.getScope())){
                Object o = singletonMap.get(beanDefinition.getId());
                if (beanDefinition.getDestroyMethod()!=null){
                    try {
                        beanDefinition.getDestroyMethod().invoke(o);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    //执行初始化方法
    public void executeInit(Object o,BeanDefinition beanDefinition){
        Method initMethod = beanDefinition.getInitMethod();
        if (initMethod!=null){
            try {
                initMethod.invoke(o);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    public Object getBean(String name) {
        return doGetBean(name);
    }

    public <T>T getBean(String name,Class<T> clazz){
        return (T)doGetBean(name);
    }

    public Object doGetBean(String name) {
        Object bean = singletonMap.get(name);
        if (bean==null){
            bean = createBean(prototype.get(name));
        }
        return bean;
    }

    public void close(){
        executeDestroy();
        singletonMap.clear();
        prototype.clear();
    }

}
